10.2 Training SVM mit Scikit-Learn

10.2 Training SVM mit Scikit-Learn#

Wenn wir in der Dokumentation von Scikit-Learn Scikit-Learn/SVM die Support Vector Machines nachschlagen, so finden wir viele Einträge: SVC, NuSVC, LinearSVC, SVR, NuSVR und LinearSVR. Die Varianten mit “C” stehen für Klassifikation (englisch Classification) und die Varianten mit “R” für Regression. Wir benutzen in diesem Kapitel das Modell SVC.

Lernziele#

Lernziele

  • Sie können ein SVM-Modell mit der Klasse SVC erzeugen.

  • Sie können den Parameter C zur Steuerung der Margins einsetzen.

Wahl des linearen Kernels#

Zuerst importieren wir aus Scikit-Learn das entsprechende Modul ‘SVM’ und instantiieren ein Modell. Da wir die etwas allgemeinere Klasse SVC anstatt LinearSVC verwenden, müssen wir bereits bei der Erzeugung die Option kernel= auf linear setzen, also kernel='linear'.

from sklearn import svm
svm_modell = svm.SVC(kernel='linear')

Wir erzeugen uns erneut künstliche Messdaten.

from sklearn.datasets import make_blobs
import matplotlib.pylab as plt; plt.style.use('bmh')

# Erzeugung künstlicher Daten
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.50)

# Visualisierung künstlicher Daten
import plotly.express as px

fig = px.scatter(x = X[:,0], y = X[:,1],  color=y, color_continuous_scale=['#3b4cc0', '#b40426'],
                 title='Künstliche Daten',
                 labels={'x': 'Feature 1', 'y': 'Feature 2'})
fig.show()

Warnung: Datenskalierung für SVM notwendig

SVMs sind empfindlich gegenüber unterschiedlichen Feature-Skalen. Ein Feature mit Werten von 0-1000 dominiert ein Feature mit Werten 0-1, auch wenn beide gleich wichtig sind. Daher sollten Features vor dem Training skaliert werden.

Eine Skalierung der Daten ist hier nicht erforderlich, da alle Features im gleichen Wertebereich liegen. Als nächstes teilen wir die Messdaten in Trainings- und Testdaten auf.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

Nun können wir unser SVM-Modell trainieren:

svm_modell.fit(X_train, y_train);

Und als nächstes analysieren, wie viele der Testdaten mit dem trainierten Modell korrekt klassifiziert werden.

svm_modell.score(X_test, y_test)
1.0

Ein super Ergebnis! Schön wäre es jetzt noch, die gefundene Trenngerade zu visualisieren. Dazu modifizieren wir ein Code-Beispiel aus dem Buch: »Data Science mit Python« von Jake VanderPlas (mitp Verlag 2017), ISBN 978-3-95845- 695-2, siehe jakevdp/PythonDataScienceHandbook.

# Quelle: VanderPlas "Data Science mit Python", S. 482
# modified by Simone Gramsch
import numpy as np

def plot_svc_grenze(model):
    # aktuelles Grafik-Fenster auswerten
    ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    # Raster für die Auswertung erstellen
    x = np.linspace(xlim[0], xlim[1], 30)
    y = np.linspace(ylim[0], ylim[1], 30)
    Y, X = np.meshgrid(y, x)
    xy = np.vstack([X.ravel(), Y.ravel()]).T

    # Abstand zur Trennhyperebene berechnen mit eingebauter 
    # decision_function()
    P = model.decision_function(xy).reshape(X.shape)

    # Entscheidungsgrenzen und Margin darstellen
    ax.contour(X, Y, P, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])

    # Stützvektoren darstellen
    ax.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1], s=300, linewidth=1, facecolors='none', edgecolors='orange');
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)
fig, ax = plt.subplots()
ax.scatter(X[:,0], X[:,1], c=y, cmap='coolwarm')
ax.set_xlabel('Feature 1')
ax.set_ylabel('Feature 2')
ax.set_title('SVM mit Soft Margin');

plot_svc_grenze(svm_modell)
../_images/30273ffa0381587e819a677951639dd3ca0fb30b8b0672094c471653d370e7c6.png

Der Parameter C#

Im letzten Abschnitt haben wir uns mit dem Parameter C beschäftigt, der Ausnahmen innerhalb des Sicherheitsstreifens erlaubt. Wir können uns den Parameter C als die “Höhe der Mauer” um den Margin vorstellen. Eine hohe Mauer (großes C) schützt den Sicherheitsbereich streng. Praktisch keine Datenpunkte dürfen hinein. Eine niedrige Mauer (kleines C) ist toleranter und lässt mehr Ausnahmen zu. Als nächstes schauen wir uns an, wie der Parameter C gesetzt wird.

Die Option zum Setzen des Parameters C lautet schlicht und einfach C=. Dabei muss C immer positiv sein. Der voreingestellte Standardwert ist C=1.

Damit aber besser sichtbar wird, wie sich C auswirkt, vermischen wir die künstlichen Daten stärker. Für die Bewertung der Modelle trennen wir die Daten in Trainings- und Testdaten.

# Erzeugung künstlicher Daten
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.80)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# Visualisierung künstlicher Daten
import plotly.express as px

fig = px.scatter(x = X[:,0], y = X[:,1],  color=y, color_continuous_scale=['#3b4cc0', '#b40426'],
                 title='Künstliche Daten',
                 labels={'x': 'Feature 1', 'y': 'Feature 2'})
fig.show()

Zuerst wählen wir ein sehr hohes C=1000000. Das ist ein Hard Margin, eine praktisch nicht durchdringbare Mauer, die keine Datenpunkte in den Sicherheitsbereich lässt.

# Wahl des Modells mit linearem Kern und großem C
svm_modell = svm.SVC(kernel='linear', C=1000000)

# Training und Bewertung
svm_modell.fit(X_train, y_train);
score = svm_modell.score(X_test, y_test)
print(f'Score auf den Testdaten: {score:.2f}')

# Visualisierung
fig, ax = plt.subplots()
ax.scatter(X[:,0], X[:,1], c=y, cmap='coolwarm')
ax.set_xlabel('Feature 1')
ax.set_ylabel('Feature 2')
ax.set_title('SVM mit Hard Margin');
plot_svc_grenze(svm_modell)
Score auf den Testdaten: 1.00
../_images/3cad14f29c9804c89537261ceffb1655faf76a5871eccb71d90feb819c880aaa.png

Nun reduzieren wir den Wert des Parameters C deutlich auf C=1. Vereinzelt liegen Datenpunkte nun im Sicherheitsbereich, d.h. wir haben einen Soft Margin.

# Wahl des Modells mit linearem Kern und kleinem C
svm_modell = svm.SVC(kernel='linear', C=1)

# Training und Bewertung
svm_modell.fit(X_train, y_train);
score = svm_modell.score(X_test, y_test)
print(f'Score auf den Testdaten: {score:.2f}')

# Visualisierung
fig, ax = plt.subplots()
ax.scatter(X[:,0], X[:,1], c=y, cmap='coolwarm')
ax.set_xlabel('Feature 1')
ax.set_ylabel('Feature 2')
ax.set_title('SVM mit Soft Margin');
plot_svc_grenze(svm_modell)
Score auf den Testdaten: 1.00
../_images/193a7c928e656728c9cc85a5d69457e72d1e0ebafcff961206335a7cb312aa2e.png

Die Visualisierung zeigt, dass bei kleinem C mehr Stützvektoren (orange eingekreist) vorhanden sind, und einige davon innerhalb des Margins liegen oder die Klassifikationsgrenze verletzen.

Welches C sollen wir wählen? Beide Modelle liefern den gleichen Score von 1.0 auf den Testdaten. Das kleinere C führt jedoch zu einem robusteren Modell mit breiterem Margin. In der Praxis wird C oft durch Kreuzvalidierung (Crossvalidation) optimiert, was wir in einem späteren Kapitel aufgreifen werden.

Zusammenfassung#

Verwenden wir den SVC-Klassifikator aus dem Modul SVM von Scikit-Learn, können wir mittels der Option kernel='linear' eine binäre Klassifikation durchführen, bei der die Trennungsgerade den größtmöglichen Abstand zwischen den Gruppen von Punkten erzeugt, also einen möglichst großen Margin. Sind die Daten nicht linear trennbar, so können wir mit der Option C= steuern, wie viele Ausnahmen erlaubt werden sollen. Mit Ausnahmen sind Punkte innerhalb des Margins gemeint. Im nächsten Abschnitt betrachten wir nichtlineare Trennungsgrenzen.